/*
 Copyright (C) 2009 QDSS.org
 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.qdss.topisv.api;

import java.io.IOException;
import java.io.StringWriter;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.security.MessageDigest;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.math.NumberUtils;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.XMLWriter;

/**
 * Api工具类
 * 
 * @author <a href="mailto:zhaofang123@gmail.com">FANGFANG ZHAO</a>
 * @since 0.1.0, Nov 17, 2009
 * @version $Id: ApiUtils.java 73 2013-06-25 16:20:32Z zhaofang123 $
 */
public class ApiUtils {

	public static final String ENCODING_UTF8 	= "UTF-8";
	public static final String ENCODING_GBK 	= "GBK";
	public static final String ENCODING_DEFAULT = ENCODING_UTF8;
	
	public static final String REST_URL			= "http://gw.api.taobao.com/router/rest";
	public static final String REST_URL_SANDBOX	= "http://gw.api.tbsandbox.com/router/rest";
	public static final String REST_URL_FUCK	= "http://tapi.qidapp.cn/router/rest";
	
	public static final String SP_APP_KEY		= "app_key";
	public static final String SP_APP_SECRET	= "app_secret";
	
	public static final String SP_SIGN 			= "sign";
	public static final String SP_METHOD 		= "method";
	public static final String SP_SESSION 		= "session";
	public static final String SP_TIMESTAMP 	= "timestamp";
	
	/**
	 * 转换参数到符合调用规范的URI(TOP)
	 * 
	 * @param url
	 * @param params
	 * @return
	 */
	public static String toApiInvokeURI(String url, Map<String, String> params, Set<String> notInUriParamNames) {
		// 签名
		final String ab_app_secret = params.remove(SP_APP_SECRET);
		if (params.containsKey("REST_URL_FUCK")) {
			url = params.remove("REST_URL_FUCK");
		}
		Map<String, String> treeParams = new TreeMap<String, String>(params);
		
		StringBuffer sign = new StringBuffer(ab_app_secret);
        for (Map.Entry<String, String> e : treeParams.entrySet()) {
    		sign.append(e.getKey()).append(e.getValue());
        }
        sign.append(ab_app_secret);
        params.put(SP_SIGN, toMD5(sign.toString()));
        
        // 组装
        StringBuffer uri = new StringBuffer(url);
		uri.append('?');
		boolean first = true;
		for (Map.Entry<String, String> e : params.entrySet()) {
			if (first) {
				first = false;
			} else {
				uri.append('&');
			}
			
			if (notInUriParamNames == null || !notInUriParamNames.contains(e.getKey())) {
				uri.append(e.getKey()).append('=').append(encodeURL(e.getValue()));
			}
		}
		
		return uri.toString();
	}
	
	/**
	 * MD5加密
	 * 
	 * @param source
	 * @return
	 */
	public static String toMD5(String source) {
		try {
			MessageDigest digest = MessageDigest.getInstance("MD5");
			byte[] data = digest.digest(source.getBytes(ENCODING_DEFAULT));
			return toHexString(data);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}
	
	/**
	 * 将字节数组转为HEX字符串
	 * 
	 * @param source
	 * @return
	 */
	public static String toHexString(byte[] source) {
		StringBuilder sb = new StringBuilder();
		String stmp = "";
		for (int i = 0; i < source.length; i++) {
			stmp = (Integer.toHexString(source[i] & 0XFF));
			if (stmp.length() == 1) {
				stmp = "0" + stmp;
			}
			sb.append(stmp);
		}
		return sb.toString().toUpperCase();
	}
	
	/**
	 * URL编码
	 * 
	 * @param source
	 * @return
	 */
	public static String encodeURL(Object source) {
		if (source == null) {
			return StringUtils.EMPTY;
		}
		String result = source.toString();
		try {
			result = URLEncoder.encode(result, ENCODING_DEFAULT);
		} catch (Exception ex) {
			result = StringUtils.EMPTY;
		}
		return result;
	}
	
	/**
	 * URL解码
	 * 
	 * @param source
	 * @return
	 */
	public static String decodeURL(Object source) {
		if (source == null) {
			return StringUtils.EMPTY;
		}
		String result = source.toString();
		try {
			result = URLDecoder.decode(result, ENCODING_DEFAULT);
		} catch (Exception ex) {
			result = StringUtils.EMPTY;
		}
		return result;
	}
	
	/**
	 * 格式化 XML
	 * 
	 * @param node
	 * @return
	 */
	public static String formatPrettyXml(Node node) {
        StringWriter out = new StringWriter();
        OutputFormat format = OutputFormat.createPrettyPrint();
        XMLWriter writer = new XMLWriter(out, format);
        try {
            writer.write(node);
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
        	 try {
				writer.close();
			} catch (IOException e) {}
        }
        return out.toString();
    }
	
	/**
	 * 获取结果中 totalResults 标识的大小
	 * 
	 * @param element
	 * @return
	 */
	public static int getTotalResults(Element element) {
		if (element == null) {
			return 0;
		}
		Node node = element.selectSingleNode("//total_results");
		if (node == null) {
			node = element.selectSingleNode("//totalResults");
		}
		int totalResults = NumberUtils.toInt(node == null ? "0" : node.getText(), 0);
		return totalResults;
	}
	
	/**
	 * 获取结果中 totalResults 标识的大小
	 * 
	 * @param result
	 * @return
	 */
	public static int getTotalResults(Result result) {
		if (result == null) {
			return 0;
		}
		return getTotalResults(result.rootElement());
	}
	
	/**
	 * 计算分页大小
	 * 
	 * @param total
	 * @param size
	 * @return
	 */
	public static int calcPagingSize(int total, int size) {
		return (total + size - 1) / size;
	}
	
	/**
	 * 是否包含下一页，仅在调用时指定了use_has_next为true时有效
	 * 
	 * @param element
	 * @return
	 */
	public static boolean hasNext(Element element) {
		if (element == null) {
			return false;
		}
		Node node = element.selectSingleNode("//has_next");
		return node == null ? false : "true".equalsIgnoreCase(node.getText());
	}
	
	/**
	 * @param result
	 * @return
	 * @see #hasNext(Element)
	 */
	public static boolean hasNext(Result result) {
		if (result == null) {
			return false;
		}
		return hasNext(result.rootElement());
	}
}
